package ie.icmr.scs.sms.web.controller.rest;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

@Controller
@RequestMapping("/media")
public class MediaController
{
	private static final String ALFRESCO_HOST = "localhost";
	private static final String ALFRESCO_PORT = "8080";
	
	/**
	 * Instantiates a Media object, adds it to the model and returns the test page.
	 * 
	 * @param model
	 * @return
	 */
	@RequestMapping(method = RequestMethod.GET)
	public String test(Model model)
	{
		model.addAttribute(new Media());
		
		return "media_test";
	}
	
	@RequestMapping(method = RequestMethod.POST)
	@ResponseBody
	public MediaSubmissionResult uploadMedia(Media media, BindingResult result)
	{
		// TODO: validate media object (metadata and content type of the file)
		
		return startWksSumbissionWorkflow(media);
	}
	
	/**
	 * Performs authentication in Alfresco according to the credentials and then submits the media file received with its meta data for starting the submission workflow
	 * in the Workplace Knowledge System.
	 * 
	 * This is carried out by programmatically performing requests to the Alfresco Repository RESTful API. 
	 * 
	 * @param media
	 * @return
	 */
	private MediaSubmissionResult startWksSumbissionWorkflow(Media media)
	{
		HttpClient httpClient = new DefaultHttpClient();
		httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		
		try
		{
	        String alfrescoTicket = doAlfrescoLogin(httpClient, media.getUser(), media.getPassword());
			
			// TODO: generate URL dynamically; configurable host and port
			HttpPost httpPost = new HttpPost(
					"http://" + ALFRESCO_HOST + ":" + ALFRESCO_PORT + "/alfresco/service/api/icmr/submission-workflow?alf_ticket=" + alfrescoTicket);
			
			// Create a multipart entity which will allow to submit the binary media file
			MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
			
			// Add regular form parameters
			entity.addPart("{http://www.alfresco.org/model/icmrWorkflow/1.0}workgroup", 
					new StringBody(media.getWorkGroupId(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/bpm/1.0}workflowDescription", 
					new StringBody(media.getTitle(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/icmrWorkflow/1.0}description", 
					new StringBody(media.getDescription(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/icmrWorkflow/1.0}abstract", 
					new StringBody(media.getItemAbstract(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/icmrWorkflow/1.0}anonymous", 
					new StringBody(media.getIsAnonymous().toString(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/icmrWorkflow/1.0}shareable", 
					new StringBody(media.getIsShareable().toString(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/icmrWorkflow/1.0}commentable", 
					new StringBody(media.getIsCommentable().toString(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/bpm/1.0}workflowDueDate", 
//					new StringBody(media.getWorkflowDueDate().toString(), "text/plain", StandardCharsets.UTF_8)); // TODO: format date into long ISO8601 (2013-12-12T11:50:14.964+01:00)
					new StringBody("2013-12-12T11:50:14.964+01:00", "text/plain", StandardCharsets.UTF_8)); // TODO: format date into long ISO8601 (2013-12-12T11:50:14.964+01:00)
			
			entity.addPart("{http://www.alfresco.org/model/bpm/1.0}workflowPriority", 
					new StringBody(media.getPriority().toString(), StandardCharsets.UTF_8));
			
			entity.addPart("{http://www.alfresco.org/model/bpm/1.0}sendEMailNotofications", 
					new StringBody(media.getSendEmailNotifications().toString(), StandardCharsets.UTF_8));
			
			// Attach the media file parameter
			String mediaFileTempDir = System.getProperty("java.io.tmpdir") + "/" + media.getMediaFile().getOriginalFilename();
			File mediaFileTemp = new File(mediaFileTempDir);
			media.getMediaFile().transferTo(mediaFileTemp);
			entity.addPart("item1", new FileBody(mediaFileTemp, "application/zip"));
			
			httpPost.setEntity(entity);
			
			// The POST request is performed
			EntityUtils.toString(httpClient.execute(httpPost).getEntity(), StandardCharsets.UTF_8.displayName());
			
			// TODO: parse response for the following cases:
			// Response body is JSON with {"workflowId": activiti$1320}, means OK, the workflow has been started
			// Response body is empty string "", alfresco session is gone so get new ticket
			// Response is JSON with containing error 500, it is an alfresco error (file duplicated, for example)
			
			MediaSubmissionResult result = new MediaSubmissionResult();
			result.setSuccess(true);
			result.setMessage("Submission request successfully sent. It doesn't mean the workflow is started, though; please chech it in Alfresco.");
			
			return result;
		}
		// TODO: handle exceptions properly
		catch(Exception e)
		{
			e.printStackTrace();
			
			String msg = "There was an error while submitting the content: " + e.getMessage();
			
			MediaSubmissionResult result = new MediaSubmissionResult();
			result.setSuccess(false);
			result.setMessage(msg);
			
			return result;
		}
		finally
		{
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
			httpClient.getConnectionManager().shutdown();
		}
	}
	
	private String doAlfrescoLogin(HttpClient httpClient, String user, String password) throws ClientProtocolException, IOException, ParserConfigurationException, SAXException
	{
		// TODO: we are logging in for each submission, but ideally the login should be performed once and store the alfresco ticket in the SMS session
		
		// Perform login request in which response the ticket required will be contained
		BasicResponseHandler responseHandler = new BasicResponseHandler();
		String alfrescoLoginResponse = httpClient.execute(
				new HttpGet("http://" + ALFRESCO_HOST + ":" + ALFRESCO_PORT + "/alfresco/service/api/login?u=" + user + "&pw=" + password), responseHandler);
		
		// The login response is an XML so we parse it for getting the ticket value
	    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
	    DocumentBuilder builder = factory.newDocumentBuilder();  
        Document alfrescoTicketXml = builder.parse(new InputSource(new StringReader(alfrescoLoginResponse)));
        
        return alfrescoTicketXml.getFirstChild().getTextContent();
	}
}
